﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotSpatial.Controls;
using DotSpatial.Data;
using System.Windows.Forms;
using DotSpatial.Topology;
using System.Data;
using DotSpatial.Symbology;
using System.Drawing;
using ZedGraph;

namespace SoilProfiler
{
    public class DrawProfile
    {
        private IFeature _LineFeature;
        private MapLineLayer _PathLineLayer;
        private Map uxMap;
        public IFeatureSet _feature_TIN{set; get;}
        public  ConfigurareTriangulation TrianglesLayers{set; get;}
        private ZedGraph.ZedGraphControl uxZ;
        private TabControl uxTab;
        private Databasemanagement database;
        public DrawProfile(Map map,Databasemanagement database, ConfigurareTriangulation trianglesLayers, IFeatureSet feature_TIN, ZedGraph.ZedGraphControl uxz, TabControl tab)
        {
            this.uxMap = map;
            this.database = database;
            TrianglesLayers = trianglesLayers;
            this._feature_TIN = feature_TIN;
            this.uxZ = uxz;
            this.uxTab = tab;
        }


        public void StartDrawProfile()
        {

            // remove any existing path if needed.
            this.uxMap.FunctionMode = FunctionMode.None;
            if (_PathLineLayer != null)
                uxMap.Layers.Remove(_PathLineLayer);

            _PathLineLayer = null;
            _LineFeature = null;

            uxMap.Cursor = Cursors.Cross;
            uxMap.MouseDown += map_MouseDown;
        
        }


        private void map_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                //// Encourage the user to select a raster, if they haven't done so.
                //if (!uxMap.GetRasterLayers().Any())
                //{

                //    uxMap.ZoomToMaxExtent();
                //    return;
                //}

                StartOrContinueDrawingPath(e.Location);
                // App.ProgressHandler.Progress(null, 0, "Point registered. Click again to add line segment. Right-click to finish.");
            }
            else if (e.Button == MouseButtons.Right)
            {
                EndDrawingPath();
                ShowElevation();
                // App.ProgressHandler.Progress(null, 0, "Ready.");
            }
        }


        private List<DataPRofile> GetElevation(int idpos, double dist, Coordinate coordinate)
        {
            if (_feature_TIN != null)
            {
                List<DataPRofile> value = new List<DataPRofile>();

                foreach (IFeature item in _feature_TIN.Features)
                {
                    if (item.Contains(new DotSpatial.Topology.Point(coordinate)))
                    {
                        int id = Convert.ToInt32(item.DataRow["id"]);
                        Volume vol = (from v in TrianglesLayers.VolumeInfo where v.id == id select v).ToArray()[0];
                        int i = 0;
                        foreach (LayerTriangle3D items in vol.Layers)
                        {
                            DataPRofile valprofile = new DataPRofile();
                            valprofile.dist = dist;
                            valprofile.value=items.GetValue(coordinate.X, coordinate.Y);
                            valprofile.type= items.Hguname;
                             valprofile.index=idpos;
                             valprofile.x = coordinate.X;
                             valprofile.y = coordinate.Y;
                            value.Add(valprofile);
                            i++;
                        }
                        return value;
                    }
                }

                return null;

            }
            return null;
        }


        private IFeature AddLineFeatureSetToMap()
        {
            FeatureSet lineFeatureSet = new FeatureSet(FeatureType.Line);
            lineFeatureSet.Projection = uxMap.Projection;

            // Initialize the featureSet attribute table by creating columns
            DataColumn column = new DataColumn("ID", typeof(short));
            lineFeatureSet.DataTable.Columns.Add(column);
            DataColumn column2 = new DataColumn("Number of Points", typeof(int));
            lineFeatureSet.DataTable.Columns.Add(column2);
            DataColumn column3 = new DataColumn("Description");
            lineFeatureSet.DataTable.Columns.Add(column3);

            // Add the featureSet as map layer
            _PathLineLayer = (MapLineLayer)uxMap.Layers.Add(lineFeatureSet);
            _PathLineLayer.Symbolizer = new LineSymbolizer(Color.Blue, 2);
            _PathLineLayer.LegendText = "Path Layer";

            var newList = new List<Coordinate>();
            LineString lineGeometry = new LineString(newList);

            // AddFeature creates the point and a row in the DataTable
            return lineFeatureSet.AddFeature(lineGeometry);
        }

        private void StartOrContinueDrawingPath(System.Drawing.Point mouseLocation)
        {
            Coordinate coord = uxMap.PixelToProj(mouseLocation);

            if (_LineFeature == null)
            {
                // This is the first time we see a left click; create empty line feature.
                _LineFeature = AddLineFeatureSetToMap();

                // Add first coordinate to the line feature.
                _LineFeature.Coordinates.Add(coord);

                // Set the line feature attribute. This line may have multiple points,
                // but there is only one row in the attribute table for the entire feature (line).
                _LineFeature.DataRow["ID"] = 0;
                _LineFeature.DataRow["Description"] = "Path (line)";
            }
            else
            {
                // Second or later click - add points to the existing feature


                    _LineFeature.BasicGeometry.Coordinates.Add(coord);

                    if (_LineFeature.BasicGeometry.Coordinates.Count > 1)
                    {

                    _LineFeature.ParentFeatureSet.InitializeVertices();

                    // Draw the line.
                    uxMap.ResetBuffer();

                    // Update the attribute table.
                    _LineFeature.DataRow["Number of Points"] = _LineFeature.BasicGeometry.Coordinates.Count;
                }
            }
        }

        private void EndDrawingPath()
        {
            // The path is complete.
            uxMap.ResetBuffer();
            uxMap.Cursor = Cursors.Arrow;
            uxMap.MouseDown -= map_MouseDown;
            _LineFeature = null;
        }


        private List<Coordinate> GetCoordinatesFromLine(IMapLineLayer lineLayer)
        {
            IFeatureSet featureSet = lineLayer.DataSet;

            // The coordinates should be the first feature of the feature set.
            IList<Coordinate> lineCoordinates = featureSet.Features[0].Coordinates;

            // Though the original line may only have a few points, we split
            // each line segment into many points
            List<Coordinate> pathCoordinates = new List<Coordinate>();

            for (int i = 0; i < lineCoordinates.Count - 1; i++)
            {
                Coordinate startCoord = lineCoordinates[i];
                Coordinate endCoord = lineCoordinates[i + 1];
                List<Coordinate> segmentCoordinates = SplitSegment(startCoord.X, startCoord.Y, endCoord.X, endCoord.Y);

                //add list of points from this line segment to the complete list
                pathCoordinates.AddRange(segmentCoordinates);
            }
            return pathCoordinates;
        }

        private static double GetDistance(double x1, double y1, double x2, double y2)
        {
            return Math.Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
        }

        private static List<Coordinate> SplitSegment(double startX, double startY, double endX, double endY)
        {
            const int MinimumDistanceBetweenPoints = 100;

            double points = Math.Floor(GetDistance(startX, startY, endX, endY) / MinimumDistanceBetweenPoints);
            int PointsPerSegment = (int)Math.Max(points, 1);

            double curX = startX;
            double curY = startY;
            double constXdif = ((endX - startX) / PointsPerSegment);
            double constYdif = ((endY - startY) / PointsPerSegment);

            List<Coordinate> pathPointList = new List<Coordinate>(PointsPerSegment);
            for (int i = 0; i <= PointsPerSegment; i++)
            {
                if (i == 0)
                {
                    curX = startX;
                    curY = startY;
                }
                else
                {
                    curX = curX + constXdif;
                    curY = curY + constYdif;
                }
                Coordinate coordinate = new Coordinate(curX, curY);
                pathPointList.Add(coordinate);
            }
            return pathPointList;
        }


        public static Dictionary<string, Color> ListColors(Databasemanagement database)
        {
            DataTable table = database.GetDataTable("select distinct hguname from hgus");
            Dictionary<string, Color> colorsList = new Dictionary<string, Color>();
            Color[] colors = new Color[]{ Color.RosyBrown, Color.Lavender, Color.DarkOliveGreen, Color.DarkTurquoise, 
                                              Color.DarkSalmon, Color.Brown,   Color.Beige,      Color.Maroon };

            if (table.Rows.Count>50)
            {
              throw new Exception("The maximum number of units supported are 50 ");
            }

            for (int i = 0; i < table.Rows.Count; i++)
            {

     

                 string hgunit = (Convert.ToString(table.Rows[i]["hguname"]));
                 if (i < 7)
                 {
                     colorsList.Add(hgunit, colors[i]);


                 }
                 else
                 {
                     Color v= Color.FromArgb(255-(i*5),i*5,255-(i*5));

                     colorsList.Add(hgunit, v);


                 }
            }
            return colorsList;


        }


        public List<DataPRofile> Information { set; get; }

        private void ShowElevation()
        {
            //  
            try
            {


                DataTable table = database.GetDataTable("select distinct hguname from hgus");

                Dictionary<string, Color> colorList =ListColors(database);

                // IMapRasterLayer rasterLayer = uxMap.GetRasterLayers().First();
                IMapLineLayer pathLayer = uxMap.GetLineLayers().First();
                var coords = GetCoordinatesFromLine(pathLayer);

                List<double> elevation = new List<double>();
                List<double> dist = new List<double>();

                List<DataPRofile>  information = new List<DataPRofile>();
                Information = new List<DataPRofile>();
                int values = 0;
                for (int i = 0; i < coords.Count; i++)
                {
                    information.AddRange(GetElevation(i, i * 100, coords[i]));

                    if (i==0)
                        Information.AddRange(GetElevation(i, i * 100, coords[i]));

                    if (values> coords.Count * .10)
                    {
                        Information.AddRange(GetElevation(i, i * 100, coords[i]));
                        values = 0;
                    }
                    values++;  
                }

                int inColor = 0;
                uxZ.GraphPane.CurveList.Clear();
                foreach (DataRow item in table.Rows)
                {
                   
                      string hgunit = (Convert.ToString(item["hguname"]));
                    IEnumerable<double> distances= from dis in information where dis.type== hgunit select dis.dist;
                    IEnumerable<double> elevations = from dis in information where dis.type == hgunit select dis.value;

                    if (distances.Count() > 0)
                    {

                        var p = uxZ.GraphPane.AddCurve(hgunit, distances.ToArray(), elevations.ToArray(), colorList[hgunit]);
                        p.Symbol.Type = ZedGraph.SymbolType.Circle;
                        p.Symbol.Size = 2;
                        p.Line.IsVisible = true;
                        p.Line.Fill = new Fill(colorList[hgunit]);
                        // p.Symbol.Type = ZedGraph.SymbolType.Circle;
                        uxZ.AxisChange();
                        uxZ.Refresh();
                    }
                    inColor++;

                }

            }
            catch (Exception ex)
            {
                Information = null;
            }

        }

    }

    public struct DataPRofile
    {
        public double dist;
        public double value;
        public string type;
        public int index;
        public double x;
        public double y;
    }
}
